package testApplets;

import java.util.ArrayList;
import java.util.Vector;

import peasy.PeasyCam;

import processing.core.PApplet;
import processing.core.PMatrix3D;
import processing.opengl.*;
import simpleGeom.*;

public class KMeshMirrorApplet extends PApplet {
	PeasyCam cam;
	Mesh mesh = new Mesh();

	
	float r = 0;

	Mesh[] cutMeshes;
	boolean flagFold;
	boolean flagMirror;
	boolean flagReset;
	boolean exportFlag;
	boolean flagRotate;
	public void createMesh2(){
		MeshNode[][][]g=new MeshNode[4][4][4];
		float[]xCs=new float[]{0,0.5f,3.5f,4};
		float[]yCs=new float[]{0,0.5f,3.5f,4};
		float[]zCs=new float[]{0,0.5f,3.5f,4};
		mesh=new Mesh();
		for (int x=0;x<xCs.length;x++){
			for (int y=0;y<yCs.length;y++){
				for (int z=0;z<zCs.length;z++){
					MeshNode node=new MeshNode(xCs[x],yCs[y],zCs[z]);
					g[x][y][z]=node;
					mesh.addNode(node);
				}
			}
		}
		// z
		for (int x = 0; x < xCs.length - 1; x++) {
			for (int y = 0; y < yCs.length - 1; y++) {
				for (int z = 0; z < zCs.length; z++) {
					if (y != 1 || x != 1) {
						if ((x > 0 && x < 2) || (y > 0 && y < 2) || (z == 0)
								|| (z == 3)) {
							MeshFace face1 = new MeshFace(g[x][y][z],
									g[x + 1][y][z], g[x + 1][y + 1][z],
									g[x][y + 1][z]);
							mesh.addFace(face1);
						}
					}
				}
			}
		}
		// x sides
		for (int x = 0; x < xCs.length; x++) {
			for (int y = 0; y < yCs.length - 1; y++) {
				for (int z = 0; z < zCs.length - 1; z++) {
					if (y != 1 || z != 1) {
						if ((z > 0 && z < 2) || (y > 0 && y < 2) || (x == 0)
								|| (x == 3)) {

							MeshFace face1 = new MeshFace(g[x][y][z],
									g[x][y][z + 1], g[x][y + 1][z + 1],
									g[x][y + 1][z]);
							mesh.addFace(face1);
						}
					}
				}
			}
		}
		// y sides
		for (int y = 0; y < yCs.length; y++) {
			for (int x = 0; x < xCs.length - 1; x++) {
				for (int z = 0; z < zCs.length - 1; z++) {
					if (x != 1 || z != 1) {
						if ((z > 0 && z < 2) || (x > 0 && x < 2) || (y == 0)
								|| (y == 3)) {

							MeshFace face1 = new MeshFace(g[x][y][z],
									g[x + 1][y][z], g[x + 1][y][z + 1],
									g[x][y][z + 1]);
							mesh.addFace(face1);

						}
					}
				}
			}
		}

	}
	public void createMesh(){
		MeshNode[][][]g=new MeshNode[4][4][4];
		float[]xCs=new float[]{0,0.5f,3.5f,4};
		float[]yCs=new float[]{0,0.5f,3.5f,4};
		float[]zCs=new float[]{0,0.5f,3.5f,4};
		 mesh=new Mesh();
		for (int x=0;x<xCs.length;x++){
			for (int y=0;y<yCs.length;y++){
				for (int z=0;z<zCs.length;z++){
					MeshNode node=new MeshNode(xCs[x],yCs[y],zCs[z]);
					g[x][y][z]=node;
					mesh.addNode(node);
				}
			}
		}
		// top, bottom
		for (int x=0;x<xCs.length-1;x++){
			for (int y=0;y<yCs.length-1;y++){
					MeshFace face1=new MeshFace(g[x][y][0],g[x+1][y][0],g[x+1][y+1][0],g[x][y+1][0]);
					mesh.addFace(face1);
					MeshFace face2=new MeshFace(g[x][y][3],g[x+1][y][3],g[x+1][y+1][3],g[x][y+1][3]);
					mesh.addFace(face2);
			}
		}
		
		for (int x=0;x<3;x++){
			mesh.addFace(new MeshFace(g[x][1][1],g[x][2][1],g[x+1][2][1],g[x+1][1][1]));
			mesh.addFace(new MeshFace(g[x][1][2],g[x][2][2],g[x+1][2][2],g[x+1][1][2]));
		}
		for (int y=0;y<3;y+=2){
			mesh.addFace(new MeshFace(g[1][y][1],g[1][y+1][1],g[2][y+1][1],g[2][y][1]));
			mesh.addFace(new MeshFace(g[1][y][2],g[1][y+1][2],g[2][y+1][2],g[2][y][2]));
		}
		for (int x=0;x<3;x+=2){
			mesh.addFace(new MeshFace(g[x][1][1],g[x+1][1][1],g[x+1][1][2],g[x][1][2]));
			mesh.addFace(new MeshFace(g[x][2][1],g[x+1][2][1],g[x+1][2][2],g[x][2][2]));
		}
		for (int y=0;y<3;y+=2){
			mesh.addFace(new MeshFace(g[1][y][1],g[1][y+1][1],g[1][y+1][2],g[1][y][2]));
			mesh.addFace(new MeshFace(g[2][y][1],g[2][y+1][1],g[2][y+1][2],g[2][y][2]));
		}
		
		//x sides
		for (int y=0;y<yCs.length-1;y++){
			for (int z=0;z<zCs.length-1;z++){
				if (y!=1||z!=1){
				MeshFace face1=new MeshFace(g[0][y][z],g[0][y][z+1],g[0][y+1][z+1],g[0][y+1][z]);
				mesh.addFace(face1);
				MeshFace face2=new MeshFace(g[3][y][z],g[3][y][z+1],g[3][y+1][z+1],g[3][y+1][z]);
				mesh.addFace(face2);
				}
			}
		}
		//y sides
		for (int x=0;x<yCs.length-1;x++){
			for (int z=0;z<zCs.length-1;z++){
				if (x!=1||z!=1){
				MeshFace face1=new MeshFace(g[x][0][z],g[x+1][0][z],g[x+1][0][z+1],g[x][0][z+1]);
				mesh.addFace(face1);
				MeshFace face2=new MeshFace(g[x][3][z],g[x+1][3][z],g[x+1][3][z+1],g[x][3][z+1]);
				mesh.addFace(face2);
				}
			}
		}
		
		
	}

	public void setup() {
		size(1300, 700, OPENGL);
		
		mesh.loadMesh(this.selectInput());
		//initMesh();
		mesh.scale(1, 1, 1);
		//mesh.translate(0, 20, 0);
		cam = new PeasyCam(this, 1000f);
		// Plane plane=new Plane(new Vec3D(0,-15,0),new Vec3D(0,1,0));
		// cutMesh=maxMesh.getCopy();
		// mirror();
		/*
		 * cutMesh=plane.cutMesh(maxMesh); Mesh
		 * mirrorMesh=plane.mirror(cutMesh); cutMesh.addMesh(mirrorMesh);
		 */
		
		Vec3D normal = new Vec3D(0, 1, 0);

		Plane plane = new Plane(new Vec3D(0, 0.01f, 0), normal);
		/*cutMeshes=mesh.cutMesh(plane);
		mesh=cutMeshes[0];*/
		//mesh=fold(mesh);
		//cutMeshes[1].translate(normal.getX(), normal.getY(), normal.getZ());
	}
	
	public void initMesh(){
		createMesh();
		mesh=new Mesh();
		mesh.loadMesh("/Users/administrator/Desktop/obj/NotreDameAligned.obj");
		mesh.flipYZ();
		//mesh.triangulate();
		//mesh.createEdges();
		
		Bounds3D bounds = mesh.getBounds();

		float maxD = bounds.getMaxDimension();
		float scaleF = 100f / maxD;
		mesh.translate(-bounds.getCenterX(), -bounds.getCenterY(),
				-bounds.getCenterZ());
		mesh.scale(scaleF, scaleF, scaleF);
		mesh.triangulate();
	}
	public void draw() {
		if (flagMirror) {
			mesh=mirror(mesh);
			flagMirror = false;
		}
		if (flagFold) {
			println("fold");
			mesh=fold(mesh);
			flagFold = false;
		}
		if (flagReset) {
			initMesh();
			flagReset=false;
		}
		if (flagRotate) {
			rotate(mesh);
			flagRotate=false;
		}
		scale(0.5f);
		//r += 0.04f;
		background(0);
		scale(1.6f, -1.6f, 1.6f);

		//rotateY(r);
		directionalLight(100, 100, 100, 1, 2, 1);
		// PGraphicsHelper.drawWorldAxis3D(g, 100);
		stroke(100);
		//noStroke();
		noFill();
		lights();

		// maxMesh.display(g);
		Plane plane = new Plane(new Vec3D(0, -mouseY, 0), new Vec3D(0, 1, 0));
		// Mesh maxMesh2=plane.cutMesh(maxMesh);
		// maxMesh2.translate(5, 0, 0);
		fill(255);
		//noStroke();
		//noFill();
		//mesh.display3D(g);
		for (int i=0;i<mesh.getNFaces();i+=100){
			MeshFace face=mesh.getFace(i);
			
				fill(255);
			
			//for ()face.getNNodes()
			face.display3D(g);
			
		}
		Bounds3D br=mesh.getBounds();
		float cX=br.getCenterX();
		float cY=br.getCenterY();
		float cZ=br.getCenterZ();
		Vec3D center=new Vec3D(cX,cY,cZ);
		/*for (int i=0;i<mesh.getNEdges();i++){
			MeshEdge e=mesh.getEdge(i);
			MeshNode n1=e.getNode1();
			MeshNode n2=e.getNode2();
			Vec3D v1=Vec3D.getBetween(center, n1, 1.05f);
			Vec3D v2=Vec3D.getBetween(center, n2, 1.05f);
			beginShape(QUADS);
			vertex(n1.getX(),n1.getY(),n1.getZ());
			vertex(n2.getX(),n2.getY(),n2.getZ());
			vertex(v2.getX(),v2.getY(),v2.getZ());
			vertex(v1.getX(),v1.getY(),v1.getZ());
			endShape();
		}*/
		// ArrayList<KEdge>edges=//mesh.get
		if (exportFlag) {
			println("doExport");
			mesh.saveMesh("mirrorMesh.obj");
			/*SimpleOBJExporterFast objOut = new SimpleOBJExporterFast(
					this.selectOutput());
			this.beginRaw(objOut);
			//mesh.display3D(g);
			
			for (int i=0;i<mesh.getNFaces();i++){
				MeshFace face=mesh.getFace(i);
				/*if ("red".equals(face.getMaterial())){
					//objOut.addGroup("red");
					objOut.fillMaterial(255,0,0);
					face.display3D(g);
				}*/		
			
			/*for (int i=0;i<mesh.getNFaces();i++){
				MeshFace face=mesh.getFace(i);
				
					//objOut.addGroup("green");
					objOut.fillMaterial(255,255,0);
					face.display3D(g);
					
			}
			this.endRaw();*/
			//objOut.dispose();
			exportFlag = false;
		}

	}

	public void rotate(Mesh mesh){
		Vec3D o=new Vec3D(0,0,0);
		Vec3D vec=new Vec3D(0,0,1);
		float angle=HALF_PI;
		PMatrix3D matrix3D=new PMatrix3D();
		matrix3D.translate(o.getX(), o.getAngleY(), o.getZ());
		
		for (int i=0;i<mesh.getNNodes();i++){
			MeshNode node=mesh.getNode(i);
			//Vec3D.ro
			node.rotateAroundLine( o, vec, angle);
		}
	}
	public Mesh fold(Mesh mesh){
		Mesh newMesh=new Mesh();
		Bounds3D b=mesh.getBounds();
		Vec3D origin=b.getCenter();
		//Vec3D c=origin.getCopy();
		float offset=20;
		//float randomOffset=
		Vec3D c=new Vec3D(origin.getX(),origin.getY(),origin.getZ());
		//c.add(rndShift);
		//Vec3D normal = new Vec3D(3, random(-3,3), 0);
		Vec3D normal = new Vec3D(Vec3D.getPolar(random(6), 3));
		normal.setZ(random(-1,-4));
		float rnd=random(-5,5);
		float rndZ=rnd;
		if (random(1)>0.5){
			rndZ*=-1;
		}
		normal=new Vec3D(rnd,0,rnd);
		normal.setLength(random(90));
		if (random(1)>0.5f)normal.mult(-1);
		Vec3D shiftNormal=Vec3D.mult(normal, 1.9f);
		
		Plane plane = new Plane(c, normal);
		c.add(normal);
		Plane plane2 = new Plane(c, normal);
		Mesh[] splitMeshes=mesh.cutMesh(plane);
		if (splitMeshes[0]==null||splitMeshes[1]==null)return mesh;
		// splitting in 3 parts: m1,mTemp,m3
		splitMeshes[0].triangulate();
		splitMeshes[1].triangulate();
		
		Mesh m1=splitMeshes[0];
		Mesh mTemp=splitMeshes[1];
		Mesh[] splitMeshes2=mTemp.cutMesh(plane2);
		if (splitMeshes2[0]==null||splitMeshes2[1]==null)return mesh;
		Mesh m2=splitMeshes2[1];
		m2.translate(shiftNormal.getX(), shiftNormal.getY(), shiftNormal.getZ());
		
		Mesh mMiddle1=splitMeshes2[0];
		Mesh mMiddle2=mMiddle1.getMirrored(plane2);
		c.add(normal);
		Plane plane3 = new Plane(c, normal);
		Mesh mMiddle3=mMiddle2.getMirrored(plane3);
		
		
		// fusing
		newMesh.addMesh(m1);
		newMesh.addMesh(mMiddle1);
		newMesh.addMesh(mMiddle2);
		newMesh.addMesh(mMiddle3);
		newMesh.addMesh(m2);
		newMesh.rebuildIds();
		//newMesh.createEdges();
		newMesh.triangulate();
		newMesh.normalise(200f);
		return newMesh;
	}


	public Mesh mirror(Mesh mesh) {
		println("mirro");
		Bounds3D b=mesh.getBounds();
		float rX=random(-5, 5);
		float rY= random(-5, 5);
		float d=sqrt(rX*rX+rY*rY);
		Vec3D normal = new Vec3D(1, 0,1);
		Vec3D center=b.getCenter();
		center.setX(b.getX2()-50);
		Plane plane = new Plane( center, normal);
		Mesh[] meshes=mesh.cutMesh(plane);
		Mesh mesh1=meshes[0];
		
		mesh1.triangulate();
		//Mesh cMesh=plane.cutMesh(mesh);
		Mesh mirrorMesh=mesh1.getMirrored(plane);
		 
		mesh1.addMesh(mirrorMesh);
		mesh1.rebuildIds();
		//mesh1.createEdges();
		mesh1.triangulate();
		
		mesh1.normalise(200f);
		
		
		
		return mesh1;
		// cMesh.addMesh(mirrorMesh);
		// cutMesh=cMesh;
		

	}
	
	

	public void keyPressed() {
		if (key == 'm') {
			flagMirror = true;
		}
		if (key == 'f') {
			flagFold = true;
		}
		if (key == 'e') {
			exportFlag = true;
		}
		if (key == 'r') {
			flagReset = true;
		}
		if (key == 'g') {
			flagRotate = true;
		}
	}

}
